package edu.gatech.cs2340.teamrocket.model.storage;

import java.util.EnumMap;
import java.util.Map;
import java.text.DecimalFormat;
import java.io.Serializable;
import edu.gatech.cs2340.teamrocket.control.exception.InvalidTransactionException;
import edu.gatech.cs2340.teamrocket.model.item.Item;

/**
 * Basic Storage class to hold Items.
 *
 * @author Joe Tacheron
 */
public class BasicStorage implements Storage, Consumer, Supplier, Serializable, Cloneable {

    // TODO: it is not good to have this format object as a field inside this class
    private static final DecimalFormat df = new DecimalFormat("0.00");

    // TODO: buy ratio as fields
    // TODO: buyitem, sellitem, canbuyitem, addmoney, deductmoney must take a BasicStorage as a param
    // TODO: replace clone, become with validate and commit, both taking a list of transactions

    protected String name;
    protected double money;
    protected Map<Item, Integer> items;

    public BasicStorage(String name){
        this(name, 0);
    }

    public BasicStorage(String name, double m){
        this.name = name;
        money = m;
        items = new EnumMap<Item, Integer>(Item.class);
    }

    @Override
    public void validate(){
        // check that money is non-negative
        if(money < 0)
            throw new InvalidTransactionException(
                    "<html><p>Oops! It looks like the " + name + " does not have enough money.</p></html>");

        // check that all items have non-negative quantity
        for(Item i : Item.values()){
            if(getQuantity(i) < 0)
                throw new InvalidTransactionException(
                        "<html><p>Oops! It looks like the " + name + " does not have enough " + i + ".</p></html>");
        }
    }

    @Override
    public void become(BasicStorage bs){
        this.money = bs.money;
        this.items = bs.items;
    }

    @Override
    public void setMoney(double m){
        money = m;
        if(money < 0)
            money = 0;
    }

    @Override
    public double getMoney(){
        return money;
    }

    @Override
    public void addItem(Item i){
        addItem(i, 1);
    }

    @Override
    public void addItem(Item i, int q){
        if(items.get(i) == null)
            items.put(i, 0);
        items.put(i, q + items.get(i));
    }

    @Override
    public void removeItem(Item i){
        removeItem(i, 1);
    }

    @Override
    public void removeItem(Item i, int q){
        if(q == 0)
            return;
        int amount = getQuantity(i) - q;
        items.put(i, amount);
    }

    @Override
    public void addMoney(Item i, int quantity){
        money += i.getPrice() * quantity;
    }

    @Override
    public void deductMoney(Item i, int quantity){
        money -= i.getPrice() * quantity;
    }

    @Override
    public boolean canBuyItem(Item i){
        return canBuyItem(i, 1);
    }

    @Override
    public boolean canBuyItem(Item i, int quantity){
        return quantity * i.getPrice() <= money;
    }

    @Override
    public final void buyItem(Item i) {
        buyItem(i, 1);
    }

    @Override
    public final void buyItem(Item i, int quantity) {
        addItem(i, quantity);
        deductMoney(i, quantity);
    }

    @Override
    public boolean canSellItem(Item i){
        return canSellItem(i, 1);
    }

    @Override
    public boolean canSellItem(Item i, int quantity){
        return quantity <= items.get(i);
    }

    @Override
    public final void sellItem(Item i) {
        sellItem(i, 1);
    }

    @Override
    public final void sellItem(Item i, int quantity) {
        removeItem(i, quantity);
        addMoney(i, quantity);
    }

    @Override
    public int getQuantity(Item i) {
        if(items.get(i) == null)
            return 0;
        else
            return items.get(i);
    }

    @Override
    public boolean contains(Item i) {
        return getQuantity(i) > 0;
    }

    @Override
    @Deprecated
    public BasicStorage clone(){
        // we want a deep copy
        BasicStorage retval = new BasicStorage(name, money);
        for(Map.Entry<Item, Integer> entry : items.entrySet()){
            retval.items.put(entry.getKey(), new Integer(entry.getValue()));
        }
        return retval;
    }

    public final String toHTML(){
        StringBuilder retval = new StringBuilder();
        retval.append("<html>");
        retval.append(htmlBody());
        retval.append("</html>");
        return retval.toString();
    }

    protected String htmlBody(){
        StringBuilder retval = new StringBuilder();
        retval.append("Money: ");
        retval.append("$");
        retval.append(df.format(money));
        retval.append("<p>");
        for(Item item : Item.values()){
            retval.append("(");
            retval.append(getQuantity(item));
            retval.append(") ");
            retval.append(item);
            retval.append("<p>");
        }
        return retval.toString();
    }

    @Override
    public void clear() {
        for(Map.Entry<Item, Integer> entry : items.entrySet()){
            removeItem(entry.getKey(), entry.getValue());
        }
    }
}